<script setup lang="ts">
import { ref, toRefs } from "vue";
export interface Props {
  lightList?: Light[];
}
type Light = {
  color: string;
  time: number;
};
const props = withDefaults(defineProps<Props>(), {
  lightList: () => [
    {
      color: "red",
      time: 6,
    },
    {
      color: "yellow",
      time: 2,
    },
    {
      color: "green",
      time: 5,
    },
  ],
});
const lightList = ref(props.lightList); //红灯 黄灯 绿灯

let idx = ref(0);
let lightTime = ref(0);

const delay = (ms: number) => new Promise((r) => setTimeout(r, ms));

// 等待切换
const wait = async (light: string, timeout: number) => {
  return new Promise((resolve, reject) => {
    resolve(light);
  }).then(async (res) => {
    idx.value = lightList.value.findIndex((item: Light) => item.color == res); //颜色切换
    // 倒计时
    while (timeout && timeout > 0) {
      lightTime.value = timeout;
      await delay(1000);
      timeout--;
    }
  });
};
// 红绿灯
const trafficLight = async (lights: Array<Light>) => {
  const waitList: Array<Function> = [];
  for (let i = 0; i < 3; i++) {
    const lightItem = () => wait(lights[i].color, lights[i].time);
    waitList.push(lightItem);
  }
  waitList.push(() => wait(lights[1].color, lights[1].time)); //绿灯切红灯之前的黄灯
  while (true) {
    // 循环调用切换
    for await (const cb of waitList) {
      await cb();
      lightTime.value = 0; //切换之前把数值置零
      await delay(1000);
    }
  }
};
trafficLight(lightList.value); //红 黄 绿
</script>

<template>
  <div
    v-for="(item, index) in lightList"
    style="display: flex; align-items: center; gap: 16px"
  >
    <div
      class="light"
      :class="{ flicker: idx === index && lightTime <= 3 && idx !== 1 }"
      :style="{ background: idx === index ? item.color : 'transparent' }"
    ></div>
    <div v-show="idx === index">{{ lightTime }}</div>
  </div>
</template>

<style scoped>
.light {
  width: 50px;
  height: 50px;
  border: 1px solid #666;
  border-radius: 50%;
}
.flicker {
  animation: bounce 1s linear infinite;
}
@keyframes bounce {
  0% {
    opacity: 0.2;
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0.2;
  }
}
</style>
